//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#include "cparsedword.h"
#include <buola/algorithm/split.h>
#include "../cinterpreter.h"

namespace buola { namespace bush {

std::string run_shell_string(TTokenIt pS)
{
    int lOffset=pS->mToken==TOKEN_STRING_TI?3:1;
    try
    {
        return gInterpreter->RunShellOut(std::wstring(pS->mB+lOffset,pS->mE-lOffset));
    }
    catch(...)
    {
        return std::string();
    }
}

//this function performs expansion which results in a simple word.
//it calls the multiple version, and then only takes the first one.
//it then applies tilde expansion on the expanded word

void CParsedWord::ExpandSimple(CWord &pResult)
{
    std::vector<CWord> lWords;
    ExpandMultiple(lWords,true);
    if(!lWords.empty())
        pResult=lWords[0];
}

//this performs the real expansion into (possibly) multiple words. The complete parameter
//determines whether tilde and glob expansion is applied to the result. This will be false when performing subexpansion
//and true when called from the outside to do the complete expansion

void CParsedWord::ExpandMultiple(std::vector<CWord> &pResult,bool pComplete)
{
    if(pResult.empty())
        pResult.emplace_back();

    if(mType==TYPE_NORMAL)
    {
        if(mRange.first==mRange.second) return;

        switch(mRange.first->mToken)
        {
        case TOKEN_STRING_I:
        case TOKEN_STRING_TI:
            {
                std::string lString=run_shell_string(mRange.first);
                std::vector<std::string> lWords;
                split(lString,lWords,fn::is_ascii_space());
                
                std::vector<CWord> lNew;
                
                for(int i=0;i<pResult.size();i++)
                {
                    for(int j=0;j<lWords.size();j++)
                    {
                        if(lWords[j].empty()) continue;
                        CWord lWord=pResult[i];
                        lWord.AppendRawText(lWords[j]);
                        lNew.push_back(std::move(lWord));
                    }
                }
                
                swap(lNew,pResult);
            }
            break;
        case TOKEN_DOLLAR_IDENTIFIER:
            {
                std::string lString=gInterpreter->EvalIdentifier(mRange);
                if(!lString.empty())
                    for(int i=0;i<pResult.size();i++)
                        pResult[i].AppendRawText(lString);
            }
            break;
        case TOKEN_DOLLAR_PAREN:
            {
                std::string lString=gInterpreter->EvalExpression(mRange);
                if(!lString.empty())
                    for(int i=0;i<pResult.size();i++)
                        pResult[i].AppendRawText(lString);
            }
            break;
        case TOKEN_DOLLAR_SQUARE:
            {
                std::vector<std::string> lWords=gInterpreter->EvalExpressionToList(mRange);
                
                std::vector<CWord> lNew;
                
                for(int i=0;i<pResult.size();i++)
                {
                    for(int j=0;j<lWords.size();j++)
                    {
                        CWord lWord=pResult[i];
                        lWord.AppendRawText(lWords[j]);
                        lNew.push_back(std::move(lWord));
                    }
                }
                
                swap(lNew,pResult);
            }
            break;
        case TOKEN_OP_DOLLAR:
            {
                TTokenIt lS=mRange.first+1;
                if(lS==mRange.second) return;
                if(lS->mToken!=TOKEN_STRING_I&&lS->mToken!=TOKEN_STRING_TI) return;

                std::string lString=run_shell_string(lS);
                for(int i=0;i<pResult.size();i++)
                    pResult[i].AppendRawText(lString);
            }
            break;
        default:
            for(int i=0;i<pResult.size();i++)
                pResult[i].AppendTokens(mRange);
        }
    }
    else if(mType==TYPE_MULTIPLE)
    {
        for(int i=0;i<mChildren.size();i++)
        {
            mChildren[i].ExpandMultiple(pResult,false);
        }
    }
    else if(mType==TYPE_BRACES)
    {
        std::vector<CWord> lNew;
        
        for(int i=0;i<mChildren.size();i++)
        {
            std::vector<CWord> lPart;
            
            mChildren[i].ExpandMultiple(lPart,false);
            
            for(int j=0;j<pResult.size();j++)
                for(int k=0;k<lPart.size();k++)
                    lNew.emplace_back(pResult[j],lPart[k]);
        }

        swap(lNew,pResult);
    }
    
    if(pComplete)
    {
        //perform tilde expansion
        for(int w=0;w<pResult.size();w++)
        {
            pResult[w].ExpandTilde();
        }
        
        //perform glob expansion
        for(int w=0;w<pResult.size();w++)
        {
            CWord &lW=pResult[w];

            if(!(lW.mFormat&CWord::FORMAT_GLOB)) continue;

            std::vector<std::string> lResult;
            
            io::glob(lW.mChars,io::GLOB_SORT,lResult);
            
            if(!lResult.size()) continue;
            
            lW.mChars=lResult[0];
            for(int i=1;i<lResult.size();i++)
            {
                CWord lNew;
                lNew.mChars=lResult[i];
                pResult.insert(pResult.begin()+w+i,lNew);
            }

            w+=lResult.size()-1;
        }
    }
}

/*namespace bush*/ } /*namespace buola*/ }
